package com.hjj.homieMatching.service.ws;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.google.gson.Gson;
import com.hjj.homieMatching.config.HttpSessionConfig;
import com.hjj.homieMatching.model.domain.Chat;
import com.hjj.homieMatching.model.domain.Team;
import com.hjj.homieMatching.model.domain.User;
import com.hjj.homieMatching.model.request.MessageRequest;
import com.hjj.homieMatching.model.vo.ChatMessageVO;
import com.hjj.homieMatching.model.vo.WebSocketVO;
import com.hjj.homieMatching.service.ChatService;
import com.hjj.homieMatching.service.TeamService;
import com.hjj.homieMatching.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import static com.hjj.homieMatching.constant.ChatConstant.*;
import static com.hjj.homieMatching.constant.UserConstant.ADMIN_ROLE;

/**
 * WebSocket服务
 */
@Component
@Slf4j
@ServerEndpoint(value = "/websocket/{userId}/{teamId}", configurator = HttpSessionConfig.class)
public class WebSocket {
    /**
     * 保存队伍的连接信息
     */
    private static final Map<String, ConcurrentHashMap<String, WebSocket>> ROOMS = new HashMap<>();

    /**
     * 线程安全的无序的集合
     */
    private static final CopyOnWriteArraySet<Session> SESSIONS = new CopyOnWriteArraySet<>();

    /**
     * 会话池
     */
    private static final Map<String, Session> SESSION_POOL = new HashMap<>(0);
    /**
     * 用户服务
     */
    private static UserService userService;
    /**
     * 聊天服务
     */
    private static ChatService chatService;
    /**
     * 团队服务
     */
    private static TeamService teamService;

    /**
     * 房间在线人数
     */
    private static int onlineCount = 0;

    /**
     * 当前信息
     */
    private Session session;

    /**
     * 上网数
     *
     * @return int
     */
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     * 添加在线计数
     */
    public static synchronized void addOnlineCount() {
        WebSocket.onlineCount++;
    }

    /**
     * 子在线计数
     */
    public static synchronized void subOnlineCount() {
        WebSocket.onlineCount--;
    }

    @Resource
    public void setHeatMapService(UserService userService) {
        WebSocket.userService = userService;
    }

    @Resource
    public void setHeatMapService(ChatService chatService) {
        WebSocket.chatService = chatService;
    }

    @Resource
    public void setHeatMapService(TeamService teamService) {
        WebSocket.teamService = teamService;
    }


    /**
     * 队伍内群发消息
     *
     * @param teamId 团队id
     * @param msg    消息
     */
    public static void broadcast(String teamId, String msg) {
        ConcurrentHashMap<String, WebSocket> map = ROOMS.get(teamId);
        // keySet获取map集合key的集合  然后在遍历key即可
        for (String key : map.keySet()) {
            try {
                WebSocket webSocket = map.get(key);
                webSocket.sendMessage(msg);
            } catch (Exception e) {
                log.error("exception message", e);
            }
        }
    }

    /**
     * 发送消息
     *
     * @param message 消息
     * @throws IOException ioexception
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 开放
     *
     * @param session 会话
     * @param userId  用户id
     * @param teamId  团队id
     * @param config  配置
     */
    @OnOpen
    public void onOpen(Session session,
                       @PathParam(value = "userId") String userId,
                       @PathParam(value = "teamId") String teamId,
                       EndpointConfig config) {
        log.info("onOpen called with userId: {}, teamId: {}", userId, teamId);
        try {
            // 验证用户ID
            if (StringUtils.isBlank(userId) || "undefined".equals(userId) || "null".equals(userId)) {
                sendError(userId, "用户ID参数有误");
                return;
            }
            
            // 设置session
            this.session = session;
            
            // 规范化teamId，确保空值统一处理为null
            String normalizedTeamId = ("NaN".equals(teamId) || "undefined".equals(teamId) || "null".equals(teamId)) ? 
                                      null : teamId;
            
            // 处理团队聊天
            if (normalizedTeamId != null) {
                if (!ROOMS.containsKey(normalizedTeamId)) {
                    ConcurrentHashMap<String, WebSocket> room = new ConcurrentHashMap<>(0);
                    room.put(userId, this);
                    ROOMS.put(normalizedTeamId, room);
                    addOnlineCount();
                    log.info("创建新的聊天室：{}, 用户: {}", normalizedTeamId, userId);
                } else if (!ROOMS.get(normalizedTeamId).containsKey(userId)) {
                    ROOMS.get(normalizedTeamId).put(userId, this);
                    addOnlineCount();
                    log.info("用户: {} 加入已有聊天室: {}", userId, normalizedTeamId);
                }
            } else {
                // 处理大厅聊天
                SESSIONS.add(session);
                SESSION_POOL.put(userId, session);
                log.info("用户: {} 加入大厅聊天", userId);
                this.sendAllUsers();
            }
            
            // 发送连接成功消息
            JSONObject successMsg = new JSONObject();
            successMsg.set("type", "connection");
            successMsg.set("status", "success");
            successMsg.set("message", "WebSocket连接已建立");
            sendOneMessage(userId, successMsg.toString());
            
        } catch (Exception e) {
            log.error("WebSocket连接建立过程中出错", e);
            try {
                JSONObject errorMsg = new JSONObject();
                errorMsg.set("type", "error");
                errorMsg.set("message", "连接失败: " + e.getMessage());
                session.getBasicRemote().sendText(errorMsg.toString());
            } catch (Exception ex) {
                log.error("发送错误消息失败", ex);
            }
        }
    }

    /**
     * 关闭
     *
     * @param userId  用户id
     * @param teamId  团队id
     * @param session 会话
     */
    @OnClose
    public void onClose(@PathParam("userId") String userId,
                        @PathParam(value = "teamId") String teamId,
                        Session session) {
        try {
            log.info("WebSocket连接关闭 - userId: {}, teamId: {}", userId, teamId);
                    
            // 规范化teamId，确保空值统一处理为null
            String normalizedTeamId = ("NaN".equals(teamId) || "undefined".equals(teamId) || "null".equals(teamId)) ? 
                                      null : teamId;
                                  
            if (normalizedTeamId != null && ROOMS.containsKey(normalizedTeamId)) {
                ROOMS.get(normalizedTeamId).remove(userId);
                log.info("用户 {} 已从聊天室 {} 中移除", userId, normalizedTeamId);
                if (getOnlineCount() > 0) {
                    subOnlineCount();
                }
            } else {
                if (!SESSION_POOL.isEmpty()) {
                    SESSION_POOL.remove(userId);
                    SESSIONS.remove(session);
                    log.info("用户 {} 已从大厅聊天中移除", userId);
                    sendAllUsers();
                }
            }
        } catch (Exception e) {
            log.error("WebSocket关闭过程中出错", e);
        }
    }

    /**
     * 消息
     *
     * @param message 消息
     * @param userId  用户id
     */
    @OnMessage
    public void onMessage(String message, @PathParam("userId") String userId) {
        log.info("接收到来自用户 {} 的消息: {}", userId, message);
            
        // 处理心跳消息
            if ("PING".equals(message)) {
            log.debug("收到心跳消息，响应pong");
            sendOneMessage(userId, "pong");
                return;
            }
            
        try {
            // 解析消息
            MessageRequest messageRequest = new Gson().fromJson(message, MessageRequest.class);
            
            // 参数验证
            Long fromId = Long.valueOf(userId);
            Long toId = messageRequest.getToId();
            Long teamId = messageRequest.getTeamId();
            String text = messageRequest.getText();
            Integer chatType = messageRequest.getChatType();
            
            if (StringUtils.isBlank(text)) {
                sendError(userId, "消息内容不能为空");
                return;
            }
            
            if (chatType == null) {
                sendError(userId, "聊天类型不能为空");
                return;
            }
            
            // 获取用户信息
            User fromUser = userService.getById(fromId);
            if (fromUser == null) {
                sendError(userId, "用户不存在");
                return;
            }
            
            // 根据聊天类型处理消息
            if (chatType == PRIVATE_CHAT) {
                // 私聊需要有接收者ID
                if (toId == null || toId <= 0) {
                    sendError(userId, "接收者ID无效");
                return;
            }

                privateChat(fromUser, toId, text, chatType);
                log.info("私聊消息已处理 - 发送者: {}, 接收者: {}", fromId, toId);

            } else if (chatType == TEAM_CHAT) {
                // 队伍聊天需要有队伍ID
                if (teamId == null || teamId <= 0) {
                    sendError(userId, "队伍ID无效");
                return;
            }

                        Team team = teamService.getById(teamId);
                        if (team == null) {
                            sendError(userId, "队伍不存在");
                            return;
                        }
                
                teamChat(fromUser, text, team, chatType);
                log.info("队伍聊天消息已处理 - 发送者: {}, 队伍: {}", fromId, teamId);
                
            } else if (chatType == HALL_CHAT) {
                        // 大厅聊天
                hallChat(fromUser, text, chatType);
                log.info("大厅聊天消息已处理 - 发送者: {}", fromId);
                
            } else {
                sendError(userId, "不支持的聊天类型");
            }
        } catch (Exception e) {
            log.error("处理WebSocket消息时出错", e);
                sendError(userId, "消息处理失败: " + e.getMessage());
        }
    }

    /**
     * 团队聊天
     *
     * @param user     用户
     * @param text     文本
     * @param team     团队
     * @param chatType 聊天类型
     */
    private void teamChat(User user, String text, Team team, Integer chatType) {
        saveChat(user.getId(), null, text, team.getId(), chatType);
        // 获取历史聊天记录
        Long teamId = team.getId();
        List<ChatMessageVO> chatMessageVOList = chatService.getCache(CACHE_CHAT_TEAM, String.valueOf(teamId));
        Date createTime = new Date();
        ChatMessageVO chatMessageVO = chatService.chatResult(user.getId(), null, text, chatType, createTime);
        WebSocketVO fromWebSocketVO = new WebSocketVO();
        fromWebSocketVO.setUsername(user.getUsername());
        fromWebSocketVO.setId(user.getId());
        fromWebSocketVO.setAvatarUrl(user.getAvatarUrl());
        chatMessageVO.setFromUser(fromWebSocketVO);
        chatMessageVO.setCreateTime(DateUtil.format(createTime, "yyyy年MM月dd日 HH:mm"));
        if (user.getUserRole() == ADMIN_ROLE) {
            chatMessageVO.setIsAdmin(true);
        }
        chatMessageVOList.add(chatMessageVO);
        // 保存聊天记录到redis中
        chatService.saveCache(CACHE_CHAT_TEAM, String.valueOf(teamId), chatMessageVOList);
        
        broadcast(String.valueOf(teamId), JSONUtil.toJsonStr(chatMessageVO));
    }

    /**
     * 大厅聊天
     *
     * @param user     用户
     * @param text     文本
     * @param chatType 聊天类型
     */
    private void hallChat(User user, String text, Integer chatType) {
        saveChat(user.getId(), null, text, null, chatType);
        // 获取大厅最近聊天记录
        List<ChatMessageVO> chatMessageVOList = chatService.getCache(CACHE_CHAT_HALL, null);
        Date createTime = new Date();
        ChatMessageVO chatMessageVO = chatService.chatResult(user.getId(), null, text, chatType, createTime);
        WebSocketVO fromWebSocketVO = new WebSocketVO();
        BeanUtils.copyProperties(user, fromWebSocketVO);
        chatMessageVO.setFromUser(fromWebSocketVO);
        chatMessageVO.setCreateTime(DateUtil.format(createTime, "yyyy年MM月dd日 HH:mm"));
        if (user.getUserRole() == ADMIN_ROLE) {
            chatMessageVO.setIsAdmin(true);
        }
        chatMessageVOList.add(chatMessageVO);
        // 保存聊天记录到redis中
        chatService.saveCache(CACHE_CHAT_HALL, null, chatMessageVOList);
        // 广播消息给聊天室所有的用户
        String msg = JSONUtil.toJsonStr(chatMessageVO);
        sendAllMessage(msg);
    }

    /**
     * 私聊
     *
     * @param user     用户
     * @param toId     为id
     * @param text     文本
     * @param chatType 聊天类型
     */
    private void privateChat(User user, Long toId, String text, Integer chatType) {
        saveChat(user.getId(), toId, text, null, chatType);
        Long userId = user.getId();
        User userTo = userService.getById(toId);
        List<ChatMessageVO> chatMessageVOS = getUserIdKey(toId, userId, text, chatType, user, userTo);
        // 指定私聊的用户
        String msg = JSONUtil.toJsonStr(chatMessageVOS.get(chatMessageVOS.size() - 1));
        sendOneMessage(toId.toString(), msg);
    }

    /**
     * 获取用户id键
     *
     * @param toId     为id
     * @param userId   用户id
     * @param text     文本
     * @param chatType 聊天类型
     * @param user     用户
     * @param userTo   用户
     * @return {@link List}<{@link ChatMessageVO}>
     */
    private List<ChatMessageVO> getUserIdKey(Long toId, Long userId, String text, Integer chatType, User user, User userTo) {
        // 将消息保存到redis，通过用户id区分,id小的在前面，id大的在后面
        String redisKey = CACHE_CHAT_PRIVATE;
        Long value1 = Math.min(toId, userId);
        Long value2 = Math.max(toId, userId);
        String key = value1 + String.valueOf(value2);

        List<ChatMessageVO> chatMessageVOList = chatService.getCache(redisKey, key);
        // 将私聊信息写到redis中
        Date createTime = new Date();
        // 自己发送的消息的记录
        ChatMessageVO myChatMessageVO = new ChatMessageVO();
        WebSocketVO myWebSocketVO = new WebSocketVO();
        BeanUtils.copyProperties(user, myWebSocketVO);
        myChatMessageVO.setFromUser(myWebSocketVO);
        WebSocketVO toWebSocketVO = new WebSocketVO();
        BeanUtils.copyProperties(userTo, toWebSocketVO);
        myChatMessageVO.setToUser(toWebSocketVO);
        myChatMessageVO.setText(text);
        myChatMessageVO.setIsMy(true);
        myChatMessageVO.setChatType(chatType);
        myChatMessageVO.setCreateTime(DateUtil.format(createTime, "yyyy年MM月dd日 HH:mm"));
            
        //对方接收的消息的记录
        ChatMessageVO toChatMessageVO = new ChatMessageVO();
        toChatMessageVO.setToUser(toWebSocketVO);
        toChatMessageVO.setFromUser(myWebSocketVO);
        toChatMessageVO.setText(text);
        toChatMessageVO.setChatType(chatType);
        toChatMessageVO.setCreateTime(DateUtil.format(createTime, "yyyy年MM月dd日 HH:mm"));
        // 把消息加入到list中
        chatMessageVOList.add(myChatMessageVO);
        chatService.saveCache(redisKey, key, chatMessageVOList);

        return chatMessageVOList;
    }

    /**
     * 保存聊天
     *
     * @param userId   用户id
     * @param toId     为id
     * @param text     文本
     * @param teamId   团队id
     * @param chatType 聊天类型
     */
    private void saveChat(Long userId, Long toId, String text, Long teamId, Integer chatType) {
            Chat chat = new Chat();
            chat.setFromId(userId);
            chat.setText(String.valueOf(text));
            chat.setChatType(chatType);
            chat.setCreateTime(new Date());
            if (toId != null && toId > 0) {
                chat.setToId(toId);
            }
            if (teamId != null && teamId > 0) {
                chat.setTeamId(teamId);
        }
        chatService.save(chat);
    }

    /**
     * 发送失败
     *
     * @param userId       用户id
     * @param errorMessage 错误消息
     */
    private void sendError(String userId, String errorMessage) {
        JSONObject obj = new JSONObject();
        obj.set("error", errorMessage);
        sendOneMessage(userId, obj.toString());
    }

    /**
     * 广播消息
     *
     * @param message 消息
     */
    public void sendAllMessage(String message) {
        for (Session userSession : SESSIONS) {
            try {
                if (userSession.isOpen()) {
                    synchronized (userSession) {
                        userSession.getBasicRemote().sendText(message);
                    }
                }
            } catch (Exception e) {
                log.error("exception message", e);
            }
        }
    }

    /**
     * 发送一条消息
     *
     * @param userId  用户id
     * @param message 消息
     */
    public void sendOneMessage(String userId, String message) {
        Session session = SESSION_POOL.get(userId);
        if (session != null && session.isOpen()) {
            try {
                synchronized (session) {
                    session.getBasicRemote().sendText(message);
                }
            } catch (Exception e) {
                log.error("exception message", e);
            }
        }
    }

    /**
     * 发送所有用户
     */
    public void sendAllUsers() {
        int size = SESSIONS.size();
        log.info("sendAllUsers to {} users.", size);

        // 获取所有的用户
        HashMap<String, Object> map2 = new HashMap<>();
        map2.put("users", this.getUsers());
        sendAllMessage(JSONUtil.toJsonStr(map2));
    }

    /**
     * 发送用户
     *
     * @param userId 用户id
     */
    public void sendUsers(String userId) {
        Session session = SESSION_POOL.get(userId);
        if (session != null && session.isOpen()) {
            try {
                HashMap<String, Object> map2 = new HashMap<>();
                map2.put("users", this.getUsers());
                session.getBasicRemote().sendText(JSONUtil.toJsonStr(map2));
            } catch (Exception e) {
                log.error("exception message", e);
            }
        }
    }

    /**
     * 得到用户
     *
     * @return {@link List}<{@link Serializable}>
     */
    public List<Serializable> getUsers() {
        ArrayList<Serializable> userList = new ArrayList<>();
        Set<String> userIds = SESSION_POOL.keySet();
        for (String userId : userIds) {
            userList.add(JSONUtil.parseObj(userService.getById(userId)));
        }
        return userList;
    }
}
